import { Grid } from "./grid";
import { Tile } from "./tile";

export class GameManager {
  storageManager: any;
  actuator: any;
  inputManager: any;
  startTiles: number; //开始砖块的数量
  size: any; // 网格数量 size*size
  grid: any;
  score: any;
  over: any; //游戏状态1:结束
  won: any;//游戏状态2:胜利 (超过最大2次幂)
  keepPlaying: boolean = false; //游戏状态3:继续游戏
  limit: number = 11//最大幂数

  constructor(size: any, inputManager: any, actuator: any, storage: any) {
    this.size = size;
    this.inputManager = inputManager;
    this.storageManager = storage;
    this.actuator = actuator;

    this.startTiles = 2;

    this.inputManager.on("move", this.move.bind(this));//将 GameManager 类的 move 方法绑定到 inputManager 对象的 "move" 事件上

    this.setup();
  }

  // Restart the game
  restart() {
    this.storageManager.clearGameState();
    this.actuator.continueGame(); // Clear the game won/lost message
    this.setup();
  }

  // Keep playing after winning (allows going over 2048)
  playing() {
    this.keepPlaying = true;
    this.actuator.continueGame(); // Clear the game won/lost message
  }

  // 判断游戏是否结束
  isGameTerminated() {
    return this.over || (this.won && !this.keepPlaying);
  }

  // Set up the game
  setup() {
    const previousState = this.storageManager.getGameState();

    // Reload the game from a previous game if present
    if (previousState) {
      this.grid = new Grid(previousState.grid.size, previousState.grid.cells); // Reload grid
      this.score = previousState.score;
      this.over = previousState.over;
      this.won = previousState.won;
      this.keepPlaying = previousState.keepPlaying;
    } else {
      this.grid = new Grid(this.size);
      this.score = 0;
      this.over = false;
      this.won = false;
      this.keepPlaying = false;

      // Add the initial tiles
      this.addStartTiles();
    }

    // 更新页面和持久化状态
    this.actuate();
  }

  // Set up the initial tiles to start the game with
  addStartTiles() {
    for (let i = 0; i < this.startTiles; i++) {
      this.addRandomTile();
    }
  }

  // Adds a tile in a random position
  addRandomTile() {
    if (this.grid.cellsAvailable()) {
      const value = Math.random() < 0.9 ? 2 : 4;
      const tile = new Tile(this.grid.randomAvailableCell(), value);

      this.grid.insertTile(tile);
    }
  }

  // 更新页面和持久化状态
  actuate() {
    if (this.storageManager.getBestScore() < this.score) {
      this.storageManager.setBestScore(this.score);
    }

    // Clear the state when the game is over (game over only, not win)
    if (this.over) {
      this.storageManager.clearGameState();
    } else {
      this.storageManager.setGameState(this.serialize());
    }

    this.actuator.actuate(this.grid, {
      score: this.score,
      over: this.over,
      won: this.won,
      bestScore: this.storageManager.getBestScore(),
      terminated: this.isGameTerminated()
    });
  }

  // Represent the current game as an object
  serialize() {
    return {
      grid: this.grid.serialize(),
      score: this.score,
      over: this.over,
      won: this.won,
      keepPlaying: this.keepPlaying
    };
  }

  // 移动前重置砖块位置更新mergedFrom和previousPosition
  prepareTiles() {
    this.grid.eachCell((x: any, y: any, tile: { mergedFrom: null; savePosition: () => void; }) => {
      if (tile) {
        tile.mergedFrom = null;
        tile.savePosition();
      }
    });
  }

  // Move a tile and its representation
  moveTile(tile: { x: number; y: number; updatePosition: (arg0: any) => void; }, cell: { x: number; y: number; }) {
    this.grid.cells[tile.x][tile.y] = null;
    this.grid.cells[cell.x][cell.y] = tile;
    tile.updatePosition(cell);
  }

  // Move tiles on the grid in the specified direction
  move(direction: number) {
    // 0: up, 1: right, 2: down, 3: left
    const self = this;

    // 判断游戏是否结束
    if (this.isGameTerminated()) return;

    let cell, tile;

    const vector = this.getVector(direction);// 获取方向向量
    const traversals = this.buildTraversals(vector); // 获取移动方向上遍历网格的顺序

    let moved = false;

    // 移动前重置砖块位置,更新mergedFrom和previousPosition
    this.prepareTiles();

    // Traverse the grid in the right direction and move tiles
    traversals.x.forEach((x: any) => {
      traversals.y.forEach((y: any) => {
        cell = { x, y };
        tile = self.grid.cellContent(cell);

        if (tile) {
          // 移动方向上的最远位置
          const positions = self.findFarthestPosition(cell, vector);
          const next = self.grid.cellContent(positions.next);
          /*
           * 合并逻辑：
           * 1. 下一个单元格存在且值相同，则合并两个单元格，否则移动到最远位置(next && next.value === tile.value)
           * 2. 合成的方块不能与其他方块合并(!next.mergedFrom)
           * 3. 每次移动完成,在可用单元格范围内新增随机一个方块
           * 4. 判断是否胜利(merged.value === 2048)
           * 5. 判断是否结束,通过是否有可用单元格和是否存在相邻的可以合并的瓷砖对
           */
          if (next && next.value === tile.value && !next.mergedFrom) {
            // 合并
            const merged: any = new Tile(positions.next, tile.value * 2);
            merged.mergedFrom = [tile, next];

            // 更新单元格
            self.grid.insertTile(merged);
            self.grid.removeTile(tile);

            // 更新当前单元格位置
            tile.updatePosition(positions.next);

            // 更新分数
            self.score += merged.value;

            // 判断是否胜利 Math.pow(2, this.limit) 2的幂次
            if (merged.value === Math.pow(2, this.limit)) self.won = true;
          } else {
            // 移动到最远位置
            if (positions.farthest) self.moveTile(tile, positions.farthest);
          }

          if (!self.positionsEqual(cell, tile)) {
            moved = true; //移动完成
          }
        }
      });
    });

    if (moved) {
      // 移动完成添加1个随机方块
      this.addRandomTile();
      console.log('this.movesAvailable()', this.movesAvailable());
      if (!this.movesAvailable()) {
        this.over = true; // Game over!
      }
      // 更新页面和持久化状态
      this.actuate();
    }
  }

  // 获取方向向量
  getVector(direction: number) {
    // 0:上,1:右,2:下,3:左
    const map: any = {
      0: { x: 0, y: -1 }, // Up
      1: { x: 1, y: 0 }, // Right
      2: { x: 0, y: 1 }, // Down
      3: { x: -1, y: 0 } // Left
    };

    return map[direction];
  }

  buildTraversals(vector: { x: number; y: number; }) {
    const traversals: any = { x: [], y: [] };

    for (let pos = 0; pos < this.size; pos++) {
      traversals.x.push(pos);
      traversals.y.push(pos);
    }

    if (vector.x === 1) traversals.x = traversals.x.reverse(); //向右
    if (vector.y === 1) traversals.y = traversals.y.reverse(); //向下

    /*
      0: { x: 0, y: -1 }, // Up {x:[0,1,2,3],y:[0,1,2,3]}从最上面一行开始{0,0} 保持不变，从上到下遍历
      1: { x: 1, y: 0 }, // Right {x:[3,2,1,0],y:[0,1,2,3]}从最右边一列开始{3,0} 从右到左遍历
      2: { x: 0, y: 1 }, // Down {x:[0,1,2,3],y:[3,2,1,0]}从最下面一行开始{0,3} 从下到上遍历
      3: { x: -1, y: 0 } // Left {x:[0,1,2,3],y:[0,1,2,3]}从最左边一列开始{0,0} 保持不变，从左到右遍历
    */

    return traversals;
  }

  findFarthestPosition(cell: { x: any; y: any; }, vector: { x: any; y: any; }) {
    let previous;

    // 方向上，遇到边界或已占用就停止
    do {
      previous = cell; // 记录上一次移动到的位置
      cell = { x: previous.x + vector.x, y: previous.y + vector.y }; //计算下一个位置
      // console.log(previous, cell);
    } while (this.grid.withinBounds(cell) && this.grid.cellAvailable(cell));

    return {
      farthest: previous,
      next: cell // Used to check if a merge is required
    };
  }

  movesAvailable() {
    return this.grid.cellsAvailable() || this.tileMatchesAvailable();
  }

  // 检查该单元格中的瓷砖是否可以与相邻的瓷砖合并
  tileMatchesAvailable() {
    const self = this;

    let tile;

    for (let x = 0; x < this.size; x++) {
      for (let y = 0; y < this.size; y++) {
        // 双重循环: 遍历整个网格中的每个单元格
        tile = this.grid.cellContent({ x, y });

        if (tile) {
          for (let direction = 0; direction < 4; direction++) {
            // 0:上,1:右,2:下,3:左
            const vector = self.getVector(direction);
            const cell = { x: x + vector.x, y: y + vector.y };//方向上相邻单元格

            const other = self.grid.cellContent(cell);

            if (other && other.value === tile.value) {
              return true; // 表示还是可以合并
            }
          }
        }
      }
    }

    return false;
  }

  positionsEqual(first: { x: any; y: any; }, second: { x: any; y: any; }) {
    return first.x === second.x && first.y === second.y;
  }
}